package ca.cbc.gisdigester.digester;

import ca.cbc.giscommon.dao.EventSubcategoryDAO;
import ca.cbc.giscommon.entity.Event;
import ca.cbc.giscommon.entity.EventSubcategory;
import ca.cbc.giscommon.entity.Source;
import ca.cbc.giscommon.util.CalendarUtil;
import ca.cbc.giscommon.util.GISConfig;
import ca.cbc.giscommon.util.GeometryUtil;
import ca.cbc.giscommon.util.StringUtil;
import ca.cbc.gisdigester.eveparser.EveRSSModule;
import ca.cbc.gisdigester.geoparser.GeoRSSModule;
import ca.cbc.gisdigester.geoparser.GeoRSSUtils;
import ca.cbc.gisdigester.util.FileMobileHelper;
import ca.cbc.gisdigester.util.XmlHelper;
import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndFeed;
import com.sun.syndication.io.FeedException;
import com.sun.syndication.io.SyndFeedInput;
import com.sun.syndication.io.XmlReader;
import com.vividsolutions.jts.geom.Geometry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public enum EventDigester {
    instance;
    private Logger logger = LoggerFactory.getLogger(EventDigester.class);
    private List<EventSubcategory> eventTypes = null;

    private void addEventType(Event event, String myHamiltonCategories) {
        if (StringUtil.instance.isNullOrEmpty(myHamiltonCategories)) {
            return;
        }
        String[] myHamiltonCategoryArray;
        if (myHamiltonCategories.contains(",")) {
            myHamiltonCategoryArray = myHamiltonCategories.split(",");
        } else {
            myHamiltonCategoryArray = new String[]{myHamiltonCategories};
        }

        if (eventTypes == null) {
            eventTypes = new EventSubcategoryDAO().list();
        }

        for (String str : myHamiltonCategoryArray) {
            for (EventSubcategory et : eventTypes) {
                if (et.getFeedCategories().toLowerCase().trim().contains(str.toLowerCase().trim())) {
                    event.addEventSubcategory(et);
                }
            }
        }
    }

    /**
     * @param source -- Feed source info
     */
    public List<Event> digest(Source source) {
        if (!source.getDigestFlag()) {
            logger.info("Skipping feed {} due to digest flag is false.", source.getName());
            return null;
        }

        logger.info("@@Digesting event feed with source {}", source);
        List<Event> events = null;
        if (source.getLink().indexOf("www.filemobile.com") >= 0) {
            events = digestFileMobileFeed(source);
        } else {
            events = digestRssFeed(source);
        }
        return events;
    }

    @SuppressWarnings("unchecked")
    public List<Event> digestRssFeed(Source source) {
        List<Event> events = new ArrayList<Event>();
        String sourceLink = source.getLink();
        Geometry defaultTheGeom = source.getDefaultGeom();
        SyndFeed feed;
        try {	
            if (sourceLink.startsWith("http")) {    //input is a url
            	URLConnection connection = (new URL(sourceLink)).openConnection();
             	GISConfig.instance.changeConnectionTimeouts(connection);
                feed = new SyndFeedInput().build(new XmlReader(connection));
            } else {                            //input is a file
                feed = new SyndFeedInput().build(new File(sourceLink));
            }
            List<SyndEntry> entries = feed.getEntries();
            for (SyndEntry entry : entries) {
                //get pubDate
                Date pubDate = (entry.getPublishedDate() != null) ? entry.getPublishedDate() : feed.getPublishedDate();
                if (pubDate == null) {
                    logger.error("Must provide a pubDate when digesting!");
                    continue;
                }

                String description = (entry.getDescription() != null) ? entry.getDescription().getValue() : null;
                EveRSSModule eveRSSModule = (EveRSSModule) entry.getModule(EveRSSModule.EVERSS_URI);
                Event event = null;
                //get theGeom
                Geometry theGeom = defaultTheGeom;
                if (eveRSSModule == null) {
                    GeoRSSModule geoRSSModule = GeoRSSUtils.getGeoRSS(entry);
                    if (geoRSSModule != null) {
                        theGeom = GeometryUtil.instance.parsePoint(geoRSSModule.getLongitude(), geoRSSModule.getLatitude());
                    }

                    long start = pubDate.getTime();
                    long end = CalendarUtil.instance.getEndTimeOfDay(start);

                    event = new Event(entry.getTitle(), entry.getLink(), entry.getAuthor(), description,
                            pubDate.getTime(), null, null, null, start, end, null, null, null, null, null, null, null,
                            null, null, null, null, source, theGeom);
                } else {
                    if (eveRSSModule.getLatitude() != 0.0 && eveRSSModule.getLongitude() != 0.0) {
                        theGeom = GeometryUtil.instance.parsePoint(eveRSSModule.getLongitude(), eveRSSModule.getLatitude());
                    }

                    //Calculate end date if not exist
                    long start = (eveRSSModule.getStart() <= 0) ? pubDate.getTime() : eveRSSModule.getStart();
                    long end = eveRSSModule.getEnd();

                    if (end <= 0 || (end == start && start > 0)) {
                        end = CalendarUtil.instance.getEndTimeOfDay(start);
                    }

                    event = new Event(entry.getTitle(), entry.getLink(), entry.getAuthor(), description,
                            pubDate.getTime(), eveRSSModule.getAudience(), eveRSSModule.getPrice(),
                            eveRSSModule.getFree(), start, end, eveRSSModule.getName(), eveRSSModule.getAddress(),
                            eveRSSModule.getCity(), eveRSSModule.getProvince(), eveRSSModule.getCoordinator(),
                            eveRSSModule.getEmail(), eveRSSModule.getPhone(), eveRSSModule.getCompany(),
                            eveRSSModule.getLink(), eveRSSModule.getRepeats(), eveRSSModule.getPostalcode(),
                            source, theGeom);

                    this.addEventType(event, eveRSSModule.getCategory());
                    //logger.debug("---Digested event is {}", event);

                }
                //add default eventSubcategory
                if (source.getEventSubcategory() != null) {
                    event.addEventSubcategory(source.getEventSubcategory());
                }
                //only add event has an eventType
                if(event != null && event.getEnd().after(new Date()) && event.getEventSubcategories() != null && !event.getEventSubcategories().isEmpty()) {
                    events.add(event);
                }
            }
        } catch (IllegalArgumentException e) {
            logger.error("Error when parsing url/file {}.", sourceLink, e);
        } catch (MalformedURLException e) {
            logger.error("Error when parsing url/file {}.", sourceLink, e);
        } catch (FeedException e) {
            logger.error("Error when parsing url/file {}.", sourceLink, e);
        } catch (IOException e) {
            logger.error("Error when parsing url/file {}.", sourceLink, e);
        }

        logger.info("@@Event feed with source {} digested.", sourceLink);
        return events;
    }

    public List<Event> digestFileMobileFeed(Source source) {
        List<Event> events = null;
        String sourceLink = source.getLink();
        try {
        	URLConnection connection = (new URL(sourceLink)).openConnection();
        	GISConfig.instance.changeConnectionTimeouts(connection);
            Document doc = XmlHelper.instance.readXML(connection.getInputStream());
            events = FileMobileHelper.parseEvents(doc, source);
         
        } catch (Exception e) {
            logger.error("Error when parsing url/file {}.", sourceLink, e);
        }

        logger.info("@@Event FileMobile feed with source {} digested.", sourceLink);
        return events;
    }
}
